﻿using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Reflection;
using XCI.Extensions;
using XCI.Helper;

namespace XCI.Core
{
    public class ModelValidator
    {
        /// <summary>
        /// 验证对象,返回验证错误结果集合
        /// </summary>
        /// <param name="obj">待验证的对象</param>
        /// <returns>返回验证错误结果集合</returns>
        public virtual ICollection<ValidationResult> TryValidateObject(object obj)
        {
            var results = new List<ValidationResult>();
            TryValidateObjectRecursive(obj, results, new HashSet<object>());
            return results;
        }

        /// <summary>
        /// 验证对象某个属性,返回验证错误结果集合
        /// </summary>
        /// <param name="obj">待验证的对象</param>
        /// <param name="propertyName">待验证对象的属性名称</param>
        /// <returns>返回验证错误结果集合</returns>
        public virtual ICollection<ValidationResult> TryValidateProperty(object obj, string propertyName)
        {
            var results = new List<ValidationResult>();
            var context = new ValidationContext(obj, null, null);
            context.MemberName = propertyName;
            if (string.IsNullOrEmpty(context.DisplayName)) context.DisplayName = propertyName;

            object propertyValue = ObjectHelper.GetObjectProperty(obj, propertyName);
            Validator.TryValidateProperty(propertyValue, context, results);
            return results;
        }

        /// <summary>
        /// 验证对象,返回BoolResult
        /// </summary>
        /// <param name="obj">待验证的对象</param>
        /// <returns>返回BoolResult</returns>
        public virtual BoolResult ValidateObjectAsBoolResult(object obj)
        {
            var results = App.Validator.TryValidateObject(obj);
            if (results.Count > 0)
            {
                return new BoolResult(false, App.Handler.FormatValidateErrorMessage(results));
            }
            return BoolResult.True;
        }

        /// <summary>
        /// 验证对象某个属性,返回BoolResult
        /// </summary>
        /// <param name="obj">待验证的对象</param>
        /// <param name="propertyName">待验证对象的属性名称</param>
        /// <returns>返回BoolResult</returns>
        public virtual BoolResult ValidatePropertyAsBoolResult(object obj, string propertyName)
        {
            var results = App.Validator.TryValidateProperty(obj, propertyName);
            if (results.Count > 0)
            {
                return new BoolResult(false, App.Handler.FormatValidateErrorMessage(results));
            }
            return BoolResult.True;
        }

        /// <summary>
        /// 验证对象,如果验证未通过,则抛出ModelValidatorException
        /// </summary>
        /// <param name="obj">待验证的对象</param>
        public virtual bool ValidateObject(object obj)
        {
            var result = TryValidateObject(obj);
            if (result != null && result.Count > 0)
            {
                throw new ModelValidatorException(result);
            }

            return true;
        }

        /// <summary>
        /// 验证对象某个属性,如果验证未通过,则抛出ModelValidatorException
        /// </summary>
        /// <param name="obj">待验证的对象</param>
        /// <param name="propertyName">待验证对象的属性名称</param>
        public virtual bool ValidateProperty(object obj, string propertyName)
        {
            var result = TryValidateProperty(obj, propertyName);
            if (result != null && result.Count > 0)
            {
                throw new ModelValidatorException(result);
            }
            return true;
        }

        private static bool TryValidateObjectRecursive(object obj, List<ValidationResult> results, ISet<object> validatedObjects, IDictionary<object, object> validationContextItems = null)
        {
            //防止陷入死循环
            if (validatedObjects.Contains(obj)) return true;
            validatedObjects.Add(obj);

            bool result = Validator.TryValidateObject(obj, new ValidationContext(obj, null, validationContextItems), results, true);

            var properties = obj.GetType().GetProperties().Where(prop => prop.CanRead
                && prop.GetCustomAttributes(typeof(ValidateNested), false).Any()
                && prop.GetIndexParameters().Length == 0).ToList();

            foreach (var property in properties)
            {
                if (property.PropertyType == typeof(string) || property.PropertyType.IsValueType) continue;
                var value = obj.GetPropertyValue(property.Name);
                if (value == null) continue;

                IEnumerable<object> valueEnumerable = value as IEnumerable<object> ?? new[] { value };

                foreach (var enumObj in valueEnumerable)
                {
                    if (enumObj == null) continue;
                    var nestedResults = new List<ValidationResult>();
                    if (!TryValidateObjectRecursive(enumObj, nestedResults, validatedObjects, validationContextItems))
                    {
                        result = false;
                        foreach (var validationResult in nestedResults)
                        {
                            PropertyInfo property1 = property;
                            results.Add(new ValidationResult(validationResult.ErrorMessage, validationResult.MemberNames.Select(x => property1.Name + '.' + x)));
                        }
                    }
                }
            }

            return result;
        }
    }

    /// <summary>
    /// 嵌套校验注解
    /// </summary>
    public class ValidateNested : Attribute
    {
    }

    public class ModelValidatorException : Exception
    {
        public ModelValidatorException()
        {

        }

        public ModelValidatorException(ICollection<ValidationResult> result)
        {
            Result = result;
        }

        public ModelValidatorException(string message) : base(message)
        {

        }

        public ICollection<ValidationResult> Result { get;}
    }

}